package com.sunday.common.core.remoting.rest.response;

import com.sunday.common.core.constants.StringConstants;
import com.sunday.common.core.enums.ErrorCodeEnum;
import com.sunday.common.core.enums.SuccessCodeEnum;
import com.sunday.common.core.gson.GsonUtils;
import com.sunday.common.core.remoting.base.response.Response;
import com.sunday.common.core.remoting.rest.response.utils.ErrorCodeLevelUtils;
import com.sunday.common.core.remoting.rest.response.utils.SwapRestCode;
import com.sunday.common.core.utils.NetUtils;
import com.sunday.common.core.utils.ReflectToJsonUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.lang.Nullable;

import java.io.Serializable;
import java.util.Optional;
import java.util.function.Function;

/**
 * web 端响应
 * <p>
 * https://pro.ant.design/zh-CN/docs/request
 * <p>
 * 统一规范
 * 统一错误处理
 * 接口请求并不一定是 100% 成功的，但是正常情况下我们预期接口都是成功的，只有网络异常或者权限等问题的情况下才会出现接口请求失败。所以我们通常期望的是代码逻辑只需要考虑成功的情况，对于异常情况只要在一个地方统一处理即可。
 * <p>
 * 在最佳实践中，我们定义了一套接口格式和错误处理的规范，当失败时会统一提示错误，代码只需要考虑成功即可。你可以使用 import { request } from 'umi';  来使用最佳实践内置的请求方法来获得该能力。
 * <p>
 * export interface response {
 *   success: boolean; // if request is success
 *   data?: any; // response data
 *   errorCode?: string; // code for errorType
 *   errorMessage?: string; // message display to user
 *   showType?: number; // error display type： 0 silent; 1 message.warn; 2 message.error; 4 notification; 9 page
 *   traceId?: string; // Convenient for back-end Troubleshooting: unique request ID
 *   host?: string; // onvenient for backend Troubleshooting: host of current access server
 * }
 * <p>
 * https://umijs.org/zh-CN/plugins/plugin-request
 * <p>
 * ErrorShowType
 * import { ErrorShowType } from 'umi'; 这是一个 TypeScript 定义，定义了约定的格式中支持的 showType 的类型。
 * <p>
 * export enum ErrorShowType {
 * SILENT = 0, // 不提示错误
 * WARN_MESSAGE = 1, // 警告信息提示
 * ERROR_MESSAGE = 2, // 错误信息提示
 * NOTIFICATION = 4, // 通知提示
 * REDIRECT = 9, // 页面跳转
 * }
 *
 * @author zsy
 * @since 2022/10/14
 */
@Slf4j
@Data
@NoArgsConstructor
public class RestResponse<T> implements Serializable {

    /**
     * 响应结果
     */
    @Schema(description = "响应结果")
    private Boolean success;

    /**
     * 错误码
     */
    @Schema(description = "错误码")
    private String errorCode;

    /**
     * 错误信息
     */
    @Schema(description = "错误信息")
    private String errorMessage;

    /**
     * 方便后端故障排除:唯一的请求ID
     */
    @Schema(description = "方便后端故障排除:唯一的请求ID")
    private String traceId = MDC.get(StringConstants.TRACE_ID);

    /**
     * 响应数据
     */
    @Schema(description = "响应数据")
    private T data;

    /**
     * 错误显示类型:
     * 0 silent;
     * 1 message.warn;
     * 2 message.error;
     * 4 notification;
     * 9 page
     */
    @Schema(description = "错误显示类型: 0 silent; 1 message.warn; 2 message.error; 4 notification; 9 page")
    private Integer showType;

    /**
     * 故障处理:当前接入服务器的主机
     */
    @Schema(description = "故障处理:当前接入服务器的主机")
    private String host = NetUtils.getLocalAddress().getHostAddress();

    /**
     * 返回成功消息
     *
     * @return {@link RestResponse<T>}
     */
    public static <T> RestResponse<T> ok() {
        return ok(null);
    }

    public static <T> RestResponse<T> ok(@Nullable T body) {
        return ok(null, body);
    }

    public static <T> RestResponse<T> ok(@Nullable ShowTypeEnum showType, @Nullable T body) {
        return resultReturn(Boolean.TRUE, SuccessCodeEnum.SUCCESS.errorCode, SuccessCodeEnum.SUCCESS.errorMessage, showType, body);
    }

    /**
     * 返回失败消息
     *
     * @param errorCodeEnum
     * @return {@link RestResponse<T>}
     */
    public static <T> RestResponse<T> error(ErrorCodeEnum errorCodeEnum) {
        return error(errorCodeEnum, null, null);
    }

    public static <T> RestResponse<T> error(ErrorCodeEnum errorCodeEnum, @Nullable ShowTypeEnum showType) {
        return error(errorCodeEnum, null, showType);
    }

    public static <T> RestResponse<T> error(ErrorCodeEnum errorCodeEnum, @Nullable String errorMessage) {
        return error(errorCodeEnum, errorMessage, null);
    }

    public static <T> RestResponse<T> error(ErrorCodeEnum errorCodeEnum, @Nullable String errorMessage, @Nullable ShowTypeEnum showType) {
        assert errorCodeEnum != null : "errorCodeEnum must not be null";
        return resultReturn(Boolean.FALSE, errorCodeEnum.errorCode, StringUtils.isNotBlank(errorMessage) ? errorMessage : errorCodeEnum.errorMessage, showType, null);
    }

    /**
     * Response body 通过转换器 获取最终结果
     *
     * @param response
     * @param convert
     * @return {@link RestResponse<T>}
     */
    public static <S, T> RestResponse<T> convertResultReturn(Response<S> response, @Nullable Function<S, T> convert) {
        return convertResultReturn(response, null, convert);
    }

    public static <S, T> RestResponse<T> convertResultReturn(Response<S> response, @Nullable ShowTypeEnum showType, @Nullable Function<S, T> convert) {
        return convertResultReturn(response, null, showType, convert);
    }

    public static <S, T> RestResponse<T> convertResultReturn(Response<S> response, @Nullable String errorMessage, @Nullable ShowTypeEnum showType, @Nullable Function<S, T> convert) {
        assert response != null : "response must not be null";
        return resultReturn(
                response.isSuccess(),
                response.getErrorCode(),
                StringUtils.isNotBlank(errorMessage) ? errorMessage : response.getErrorMessage(),
                showType,
                Optional.ofNullable(response)
                        .filter(Response::isSuccess)
                        .filter(res -> ObjectUtils.isNotEmpty(res.getBody()) && ObjectUtils.isNotEmpty(convert))
                        .map(res -> convert.apply(res.getBody()))
                        .orElse(null)
        );
    }

    /**
     * 通过 Response 获取最终结果
     *
     * @param response
     * @return {@link RestResponse<T>}
     */
    public static <T> RestResponse<T> resultReturn(Response<T> response) {
        return resultReturn(response, null);
    }

    public static <T> RestResponse<T> resultReturn(Response<T> response, @Nullable ShowTypeEnum showType) {
        return resultReturn(response, null, showType);
    }

    public static <T> RestResponse<T> resultReturn(Response<T> response, @Nullable String errorMessage, @Nullable ShowTypeEnum showType) {
        assert response != null : "response must not be null";
        return resultReturn(response.isSuccess(), response.getErrorCode(), StringUtils.isNotBlank(errorMessage) ? errorMessage : response.getErrorMessage(), showType, response.getBody());
    }

    protected static <T> RestResponse<T> resultReturn(boolean success, String errorCode, String errorMessage, ShowTypeEnum showType, T body) {
        //强制进行WEB端返回错误屏蔽
        SwapRestCode swapRestCode = ErrorCodeLevelUtils.swap(success, errorCode, errorMessage);
        RestResponse<T> header = new RestResponse<T>();
        header.setSuccess(success);
        header.setErrorCode(swapRestCode.getErrorCode());
        header.setErrorMessage(swapRestCode.getErrorMessage());
        header.setShowType(showType == null ? ShowTypeEnum.ERROR_MESSAGE.getType() : showType.getType());
        header.setData(body);
        return header;
    }

    /**
     * 调用是否失败
     *
     * @return {@link boolean}
     */
    public boolean isFailure() {
        return !isSuccess();
    }

    /**
     * 调用是否成功
     *
     * @return {@link boolean}
     */
    public boolean isSuccess() {
        return SuccessCodeEnum.SUCCESS.errorCode.equals(this.errorCode);
    }

    /**
     * json 输出信息
     *
     * @return {@link String}
     */
    public String toJson() {
        return GsonUtils.DEFAULT.toJson(this);
    }

    /**
     * 优化打印
     *
     * @return {@link String}
     */
    @Override
    public String toString() {
        return ReflectToJsonUtils.toJson(this);
    }

    /**
     * 前端展示类型
     *
     * @author zsy
     * @since 2022/10/14
     */
    @Getter
    @AllArgsConstructor
    public enum ShowTypeEnum {

        SILENT(0, "不提示错误"),
        WARN_MESSAGE(1, "警告信息提示"),
        ERROR_MESSAGE(2, "错误信息提示"),
        NOTIFICATION(4, "通知提示"),
        REDIRECT(9, "页面跳转");

        private Integer type;

        private String describe;

    }

}

